package org.vsg.common.jobscheduler.factory;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.security.AccessControlException;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Iterator;
import java.util.Locale;
import java.util.Properties;

import org.quartz.JobListener;
import org.quartz.Scheduler;
import org.quartz.SchedulerConfigException;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.TriggerListener;
import org.quartz.core.JobRunShellFactory;
import org.quartz.core.QuartzScheduler;
import org.quartz.core.QuartzSchedulerResources;
import org.quartz.core.SchedulingContext;
import org.quartz.ee.jta.JTAJobRunShellFactory;
import org.quartz.ee.jta.UserTransactionHelper;
import org.quartz.impl.RemoteMBeanScheduler;
import org.quartz.impl.RemoteScheduler;
import org.quartz.impl.SchedulerRepository;
import org.quartz.impl.StdJobRunShellFactory;
import org.quartz.impl.StdScheduler;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.jdbcjobstore.JobStoreSupport;
import org.quartz.impl.jdbcjobstore.Semaphore;
import org.quartz.impl.jdbcjobstore.TablePrefixAware;
import org.quartz.simpl.RAMJobStore;
import org.quartz.simpl.SimpleThreadPool;
import org.quartz.spi.ClassLoadHelper;
import org.quartz.spi.InstanceIdGenerator;
import org.quartz.spi.JobFactory;
import org.quartz.spi.JobStore;
import org.quartz.spi.SchedulerPlugin;
import org.quartz.spi.ThreadPool;
import org.quartz.utils.ConnectionProvider;
import org.quartz.utils.DBConnectionManager;
import org.quartz.utils.PropertiesParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vsg.common.jobscheduler.provider.SpringframeworkConnectionProvider;

public class SpringSchedulerFactory implements SchedulerFactory {

	public static final String SPRING_DS_INTEGRATE  = "spring-integrate";	
	
	public static final String SPRING_DS_NAME = "spring.datasource";
	
	/**
	 * set the logger for develope code , do not use System.print(ln) to out print message.
	 */
	protected static final Logger logger = LoggerFactory.getLogger(SpringSchedulerFactory.class);	

	
	private PropertiesParser cfg;	

    private SchedulerException initException = null;	

    private String propSrc = null;
    
    private Properties properties;
    
    private javax.sql.DataSource dataSource;

    /**
     * DataSource defined 
     */

    /**
     * Create an uninitialized StdSchedulerFactory.
     */
    public SpringSchedulerFactory() {
    }

    
    
    public Properties getProperties() {
		return properties;
	}

	public void setProperties(Properties properties) {
		this.properties = properties;
	}

	/**
     * Create a StdSchedulerFactory that has been initialized via
     * <code>{@link #initialize(Properties)}</code>.
     * 
     * @see #initialize(Properties)
     */
    public SpringSchedulerFactory(Properties props) throws SchedulerException {
        initialize(props);
    }

    /**
     * Create a StdSchedulerFactory that has been initialized via
     * <code>{@link #initialize(String)}</code>.
     * 
     * @see #initialize(String)
     */
    public SpringSchedulerFactory(String fileName) throws SchedulerException {
        initialize(fileName);
    }    

    
    
   public javax.sql.DataSource getDataSource() {
		return dataSource;
	}

	public void setDataSource(javax.sql.DataSource dataSource) {
		this.dataSource = dataSource;
	}

/**
     * <p>
     * Initialize the <code>{@link org.quartz.SchedulerFactory}</code> with
     * the contenents of the <code>Properties</code> file with the given
     * name.
     * </p>
     */
    public void initialize(String filename) throws SchedulerException {
        // short-circuit if already initialized
        if (cfg != null) {
            return;
        }
        
        if (initException != null) {
            throw initException;
        }

        InputStream is = null;
        Properties props = new Properties();

        is = Thread.currentThread().getContextClassLoader().getResourceAsStream(filename);
         
        try {
            if(is != null) {
                is = new BufferedInputStream(is);
                propSrc = "the specified file : '" + filename + "' from the class resource path.";
            } else {
                is = new BufferedInputStream(new FileInputStream(filename));
                propSrc = "the specified file : '" + filename + "'";
            }
            props.load(is);
        } catch (IOException ioe) {
            initException = new SchedulerException("Properties file: '"
                    + filename + "' could not be read.", ioe);
            throw initException;
        }
        finally {
        	if(is != null)
        		try { is.close(); } catch(IOException ignore) {}
        }

        initialize(props);
    }

    /**
     * <p>
     * Initialize the <code>{@link org.quartz.SchedulerFactory}</code> with
     * the contenents of the <code>Properties</code> file opened with the
     * given <code>InputStream</code>.
     * </p>
     */
    public void initialize(InputStream propertiesStream)
        throws SchedulerException {
        // short-circuit if already initialized
        if (cfg != null) {
            return;
        }
        
        if (initException != null) {
            throw initException;
        }

        Properties props = new Properties();

        if (propertiesStream != null) {
            try {
                props.load(propertiesStream);
                propSrc = "an externally opened InputStream.";
            } catch (IOException e) {
                initException = new SchedulerException(
                        "Error loading property data from InputStream", e);
                throw initException;
            }
        } else {
            initException = new SchedulerException(
                    "Error loading property data from InputStream - InputStream is null.");
            throw initException;
        }

        initialize(props);
    }

    public void initialize() throws SchedulerException {
        // short-circuit if already initialized
        if (cfg != null) {
            return;
        }
        if (initException != null) {
            throw initException;
        }


        String requestedFile = System.getProperty(StdSchedulerFactory.PROPERTIES_FILE);
        String propFileName = requestedFile != null ? requestedFile : "quartz.properties";
        File propFile = new File(propFileName);

        Properties props = new Properties();

        InputStream in = null;

        try {
            if (propFile.exists()) {
                try {
                    if (requestedFile != null) {
                        propSrc = "specified file: '" + requestedFile + "'";
                    } else {
                        propSrc = "default file in current working dir: 'quartz.properties'";
                    }
    
                    in = new BufferedInputStream(new FileInputStream(propFileName));
                    props.load(in);
    
                } catch (IOException ioe) {
                    initException = new SchedulerException("Properties file: '"
                            + propFileName + "' could not be read.", ioe);
                    throw initException;
                }
            } else if (requestedFile != null) {
                in =
                    Thread.currentThread().getContextClassLoader().getResourceAsStream(requestedFile);
    
                if(in == null) {
                    initException = new SchedulerException("Properties file: '"
                        + requestedFile + "' could not be found.");
                    throw initException;
                }
                
                propSrc = "specified file: '" + requestedFile + "' in the class resource path.";
                
                in = new BufferedInputStream(in);
                try {
                    props.load(in);
                } catch (IOException ioe) {
                    initException = new SchedulerException("Properties file: '"
                            + requestedFile + "' could not be read.", ioe);
                    throw initException;
                }
                
            } else {
                propSrc = "default resource file in Quartz package: 'quartz.properties'";
    
                in = getClass().getClassLoader().getResourceAsStream(
                        "quartz.properties");
    
                if (in == null) {
                    in = getClass().getClassLoader().getResourceAsStream(
                            "/quartz.properties");
                }
                if (in == null) {
                    in = getClass().getClassLoader().getResourceAsStream(
                            "org/quartz/quartz.properties");
                }
                if (in == null) {
                    initException = new SchedulerException(
                            "Default quartz.properties not found in class path");
                    throw initException;
                }
                try {
                    props.load(in);
                } catch (IOException ioe) {
                    initException = new SchedulerException(
                            "Resource properties file: 'org/quartz/quartz.properties' "
                                    + "could not be read from the classpath.", ioe);
                    throw initException;
                }
            }
        } finally {
            if(in != null) {
                try { in.close(); } catch(IOException ignore) { /* ignore */ }
            }
        }

        initialize(overrideWithSysProps(props));

    }    

    /**
     * Add all System properties to the given <code>props</code>.  Will override
     * any properties that already exist in the given <code>props</code>.
     */
    private Properties overrideWithSysProps(Properties props) {
        Properties sysProps = null;
        try {
            sysProps = System.getProperties();
        } catch (AccessControlException e) {
            logger.warn(
                "Skipping overriding quartz properties with System properties " +
                "during initialization because of an AccessControlException.  " +
                "This is likely due to not having read/write access for " +
                "java.util.PropertyPermission as required by java.lang.System.getProperties().  " +
                "To resolve this warning, either add this permission to your policy file or " +
                "use a non-default version of initialize().", 
                e);
        }
        
        if (sysProps != null) {
            props.putAll(sysProps);
        }
        
        if (this.properties != null) {
        	props.putAll(this.properties);
        }
        

        return props;
    }    
    
    /**
     * <p>
     * Initialize the <code>{@link org.quartz.SchedulerFactory}</code> with
     * the contenents of the given <code>Properties</code> object.
     * </p>
     */
    public void initialize(Properties props) throws SchedulerException {
        if (propSrc == null) {
            propSrc = "an externally provided properties instance.";
        }

        this.cfg = new PropertiesParser(props);
    }	
	
    protected String getSchedulerName() {
        return cfg.getStringProperty(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME, "QuartzScheduler");
    }

    private java.lang.reflect.Method getSetMethod(String name,
            PropertyDescriptor[] props) {
        for (int i = 0; i < props.length; i++) {
            java.lang.reflect.Method wMeth = props[i].getWriteMethod();

            if (wMeth != null && wMeth.getName().equals(name)) {
                return wMeth;
            }
        }

        return null;
    }    
    
    private void setBeanProps(Object obj, Properties props)
    throws NoSuchMethodException, IllegalAccessException,
        java.lang.reflect.InvocationTargetException,
        IntrospectionException, SchedulerConfigException {
    props.remove("class");

    BeanInfo bi = Introspector.getBeanInfo(obj.getClass());
    PropertyDescriptor[] propDescs = bi.getPropertyDescriptors();
    PropertiesParser pp = new PropertiesParser(props);

    java.util.Enumeration keys = props.keys();
    while (keys.hasMoreElements()) {
        String name = (String) keys.nextElement();
        String c = name.substring(0, 1).toUpperCase(Locale.US);
        String methName = "set" + c + name.substring(1);

        java.lang.reflect.Method setMeth = getSetMethod(methName, propDescs);

        try {
            if (setMeth == null) {
                throw new NoSuchMethodException(
                        "No setter for property '" + name + "'");
            }

            Class[] params = setMeth.getParameterTypes();
            if (params.length != 1) {
                throw new NoSuchMethodException(
                    "No 1-argument setter for property '" + name + "'");
            }
            if (params[0].equals(int.class)) {
                setMeth.invoke(obj, new Object[]{new Integer(pp
                        .getIntProperty(name))});
            } else if (params[0].equals(long.class)) {
                setMeth.invoke(obj, new Object[]{new Long(pp
                        .getLongProperty(name))});
            } else if (params[0].equals(float.class)) {
                setMeth.invoke(obj, new Object[]{new Float(pp
                        .getFloatProperty(name))});
            } else if (params[0].equals(double.class)) {
                setMeth.invoke(obj, new Object[]{new Double(pp
                        .getDoubleProperty(name))});
            } else if (params[0].equals(boolean.class)) {
                setMeth.invoke(obj, new Object[]{new Boolean(pp
                        .getBooleanProperty(name))});
            } else if (params[0].equals(String.class)) {
                setMeth.invoke(obj,
                        new Object[]{pp.getStringProperty(name)});
            } else {
                throw new NoSuchMethodException(
                        "No primitive-type setter for property '" + name
                                + "'");
            }
        } catch (NumberFormatException nfe) {
            throw new SchedulerConfigException("Could not parse property '"
                    + name + "' into correct data type: " + nfe.toString());
        }
    }
}    
    
    private Class loadClass(String className) throws ClassNotFoundException {

        try {
            return Thread.currentThread().getContextClassLoader().loadClass(
                    className);
        } catch (ClassNotFoundException e) {
            return getClass().getClassLoader().loadClass(className);
        }
    }    
    
    /**
     * defined handle scheduler 
     * @return
     * @throws SchedulerException
     */
    private Scheduler instantiate() throws SchedulerException {

        if (cfg == null) {
            initialize();
        }

        if (initException != null) {
            throw initException;
        }

        JobStore js = null;
        ThreadPool tp = null;
        QuartzScheduler qs = null;
        SchedulingContext schedCtxt = null;
        DBConnectionManager dbMgr = null;
        String instanceIdGeneratorClass = null;
        Properties tProps = null;
        String userTXLocation = null;
        boolean wrapJobInTx = false;
        boolean autoId = false;
        long idleWaitTime = -1;
        long dbFailureRetry = -1;
        String classLoadHelperClass;
        String jobFactoryClass;
        
        SchedulerRepository schedRep = SchedulerRepository.getInstance();

        // Get Scheduler Properties
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        String schedName = cfg.getStringProperty(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME,
                "QuartzScheduler");

        String threadName = cfg.getStringProperty(StdSchedulerFactory.PROP_SCHED_THREAD_NAME,
                schedName + "_QuartzSchedulerThread");
        
        String schedInstId = cfg.getStringProperty(StdSchedulerFactory.PROP_SCHED_INSTANCE_ID,
        		StdSchedulerFactory.DEFAULT_INSTANCE_ID);

        if (schedInstId.equals(StdSchedulerFactory.AUTO_GENERATE_INSTANCE_ID)) {
            autoId = true;
            instanceIdGeneratorClass = cfg.getStringProperty(
            		StdSchedulerFactory.PROP_SCHED_INSTANCE_ID_GENERATOR_CLASS,
                    "org.quartz.simpl.SimpleInstanceIdGenerator");
        }
        
        userTXLocation = cfg.getStringProperty(StdSchedulerFactory.PROP_SCHED_USER_TX_URL,
                userTXLocation);
        if (userTXLocation != null && userTXLocation.trim().length() == 0) {
            userTXLocation = null;
        }

        classLoadHelperClass = cfg.getStringProperty(
        		StdSchedulerFactory.PROP_SCHED_CLASS_LOAD_HELPER_CLASS,
                "org.quartz.simpl.CascadingClassLoadHelper");
        wrapJobInTx = cfg.getBooleanProperty(StdSchedulerFactory.PROP_SCHED_WRAP_JOB_IN_USER_TX,
                wrapJobInTx);

        jobFactoryClass = cfg.getStringProperty(
        		StdSchedulerFactory.PROP_SCHED_JOB_FACTORY_CLASS, null);

        idleWaitTime = cfg.getLongProperty(StdSchedulerFactory.PROP_SCHED_IDLE_WAIT_TIME,
                idleWaitTime);
        dbFailureRetry = cfg.getLongProperty(
        		StdSchedulerFactory.PROP_SCHED_DB_FAILURE_RETRY_INTERVAL, dbFailureRetry);

        boolean makeSchedulerThreadDaemon = 
            cfg.getBooleanProperty(StdSchedulerFactory.PROP_SCHED_MAKE_SCHEDULER_THREAD_DAEMON);
        
        boolean threadsInheritInitalizersClassLoader =
        	cfg.getBooleanProperty(StdSchedulerFactory.PROP_SCHED_SCHEDULER_THREADS_INHERIT_CONTEXT_CLASS_LOADER_OF_INITIALIZING_THREAD);
        
        boolean jmxExport = cfg.getBooleanProperty(StdSchedulerFactory.PROP_SCHED_JMX_EXPORT);
        boolean jmxProxy = cfg.getBooleanProperty(StdSchedulerFactory.PROP_SCHED_JMX_PROXY);
        String jmxProxyClass = cfg.getStringProperty(StdSchedulerFactory.PROP_SCHED_JMX_PROXY_CLASS);
        String jmxObjectName = cfg.getStringProperty(StdSchedulerFactory.PROP_SCHED_JMX_OBJECT_NAME);
        
        boolean rmiExport = cfg.getBooleanProperty(StdSchedulerFactory.PROP_SCHED_RMI_EXPORT, false);
        boolean rmiProxy = cfg.getBooleanProperty(StdSchedulerFactory.PROP_SCHED_RMI_PROXY, false);
        String rmiHost = cfg.getStringProperty(StdSchedulerFactory.PROP_SCHED_RMI_HOST, "localhost");
        int rmiPort = cfg.getIntProperty(StdSchedulerFactory.PROP_SCHED_RMI_PORT, 1099);
        int rmiServerPort = cfg.getIntProperty(StdSchedulerFactory.PROP_SCHED_RMI_SERVER_PORT, -1);
        String rmiCreateRegistry = cfg.getStringProperty(
        		StdSchedulerFactory.PROP_SCHED_RMI_CREATE_REGISTRY,
                QuartzSchedulerResources.CREATE_REGISTRY_NEVER);
        String rmiBindName = cfg.getStringProperty(StdSchedulerFactory.PROP_SCHED_RMI_BIND_NAME);

        if (jmxProxy && rmiProxy) {
            throw new SchedulerConfigException("Cannot proxy both RMI and JMX.");
        }
        
        Properties schedCtxtProps = cfg.getPropertyGroup(StdSchedulerFactory.PROP_SCHED_CONTEXT_PREFIX, true);

        // If Proxying to remote scheduler, short-circuit here...
        // ~~~~~~~~~~~~~~~~~~
        if (rmiProxy) {

            if (autoId) {  
                schedInstId = StdSchedulerFactory.DEFAULT_INSTANCE_ID;
            }
                
            schedCtxt = new SchedulingContext();
            schedCtxt.setInstanceId(schedInstId);

            String uid = (rmiBindName == null) ? QuartzSchedulerResources.getUniqueIdentifier(
                    schedName, schedInstId) : rmiBindName;

            RemoteScheduler remoteScheduler = new RemoteScheduler(schedCtxt,
                    uid, rmiHost, rmiPort);

            schedRep.bind(remoteScheduler);

            return remoteScheduler;
        }

        
        // Create class load helper
        ClassLoadHelper loadHelper = null;
        try {
            loadHelper = (ClassLoadHelper) loadClass(classLoadHelperClass)
                    .newInstance();
        } catch (Exception e) {
            throw new SchedulerConfigException(
                    "Unable to instantiate class load helper class: "
                            + e.getMessage(), e);
        }
        loadHelper.initialize();
        
        // If Proxying to remote JMX scheduler, short-circuit here...
        // ~~~~~~~~~~~~~~~~~~
        if (jmxProxy) {
            if (autoId) {  
                schedInstId = StdSchedulerFactory.DEFAULT_INSTANCE_ID;
            }

            if (jmxProxyClass == null) {
                throw new SchedulerConfigException("No JMX Proxy Scheduler class provided");
            }

            RemoteMBeanScheduler jmxScheduler = null;
            try {
                jmxScheduler = (RemoteMBeanScheduler)loadHelper.loadClass(jmxProxyClass)
                        .newInstance();
            } catch (Exception e) {
                throw new SchedulerConfigException(
                        "Unable to instantiate RemoteMBeanScheduler class.", e);
            }
            
            schedCtxt = new SchedulingContext();
            schedCtxt.setInstanceId(schedInstId);
            
            if (jmxObjectName == null) {
                jmxObjectName = QuartzSchedulerResources.generateJMXObjectName(schedName, schedInstId);
            }
            
            jmxScheduler.setSchedulingContext(schedCtxt);
            jmxScheduler.setSchedulerObjectName(jmxObjectName);
                        
            tProps = cfg.getPropertyGroup(StdSchedulerFactory.PROP_SCHED_JMX_PROXY, true);
            try {
                setBeanProps(jmxScheduler, tProps);
            } catch (Exception e) {
                initException = new SchedulerException("RemoteMBeanScheduler class '"
                        + jmxProxyClass + "' props could not be configured.", e);
                initException.setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
                throw initException;
            }
            
            jmxScheduler.initialize();
            
            schedRep.bind(jmxScheduler);

            return jmxScheduler;
        }
        
        JobFactory jobFactory = null;
        if(jobFactoryClass != null) {
            try {
                jobFactory = (JobFactory) loadHelper.loadClass(jobFactoryClass)
                        .newInstance();
            } catch (Exception e) {
                throw new SchedulerConfigException(
                        "Unable to instantiate JobFactory class: "
                                + e.getMessage(), e);
            }

            tProps = cfg.getPropertyGroup(StdSchedulerFactory.PROP_SCHED_JOB_FACTORY_PREFIX, true);
            try {
                setBeanProps(jobFactory, tProps);
            } catch (Exception e) {
                initException = new SchedulerException("JobFactory class '"
                        + jobFactoryClass + "' props could not be configured.", e);
                initException
                        .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
                throw initException;
            }
        }        
        
        InstanceIdGenerator instanceIdGenerator = null;
        if(instanceIdGeneratorClass != null) {
            try {
                instanceIdGenerator = (InstanceIdGenerator) loadHelper.loadClass(instanceIdGeneratorClass)
                    .newInstance();
            } catch (Exception e) {
                throw new SchedulerConfigException(
                        "Unable to instantiate InstanceIdGenerator class: "
                        + e.getMessage(), e);
            }
            
            tProps = cfg.getPropertyGroup(StdSchedulerFactory.PROP_SCHED_INSTANCE_ID_GENERATOR_PREFIX, true);
            try {
                setBeanProps(instanceIdGenerator, tProps);
            } catch (Exception e) {
                initException = new SchedulerException("InstanceIdGenerator class '"
                        + instanceIdGeneratorClass + "' props could not be configured.", e);
                initException
                        .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
                throw initException;
            }           
        }
        
        // Get ThreadPool Properties
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        String tpClass = cfg.getStringProperty(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, null);

        if (tpClass == null) {
            initException = new SchedulerException(
                    "ThreadPool class not specified. ",
                    SchedulerException.ERR_BAD_CONFIGURATION);
            throw initException;
        }

        try {
            tp = (ThreadPool) loadHelper.loadClass(tpClass).newInstance();
        } catch (Exception e) {
            initException = new SchedulerException("ThreadPool class '"
                    + tpClass + "' could not be instantiated.", e);
            initException
                    .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
            throw initException;
        }
        tProps = cfg.getPropertyGroup(StdSchedulerFactory.PROP_THREAD_POOL_PREFIX, true);
        try {
            setBeanProps(tp, tProps);
        } catch (Exception e) {
            initException = new SchedulerException("ThreadPool class '"
                    + tpClass + "' props could not be configured.", e);
            initException
                    .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
            throw initException;
        }

        // Get JobStore Properties
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        String jsClass = cfg.getStringProperty(StdSchedulerFactory.PROP_JOB_STORE_CLASS,
                RAMJobStore.class.getName());

        if (jsClass == null) {
            initException = new SchedulerException(
                    "JobStore class not specified. ",
                    SchedulerException.ERR_BAD_CONFIGURATION);
            throw initException;
        }

        try {
            js = (JobStore) loadHelper.loadClass(jsClass).newInstance();
        } catch (Exception e) {
            initException = new SchedulerException("JobStore class '" + jsClass
                    + "' could not be instantiated.", e);
            initException
                    .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
            throw initException;
        }
        tProps = cfg.getPropertyGroup(StdSchedulerFactory.PROP_JOB_STORE_PREFIX, true, new String[] {StdSchedulerFactory.PROP_JOB_STORE_LOCK_HANDLER_PREFIX});
        try {
            setBeanProps(js, tProps);
        } catch (Exception e) {
            initException = new SchedulerException("JobStore class '" + jsClass
                    + "' props could not be configured.", e);
            initException
                    .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
            throw initException;
        }

        if (js instanceof JobStoreSupport) {
            ((JobStoreSupport)js).setInstanceId(schedInstId);
            ((JobStoreSupport)js).setInstanceName(schedName);
            
            // Install custom lock handler (Semaphore)
            String lockHandlerClass = cfg.getStringProperty(StdSchedulerFactory.PROP_JOB_STORE_LOCK_HANDLER_CLASS);
            if (lockHandlerClass != null) {
                try {
                    Semaphore lockHandler = (Semaphore)loadHelper.loadClass(lockHandlerClass).newInstance();
                    
                    tProps = cfg.getPropertyGroup(StdSchedulerFactory.PROP_JOB_STORE_LOCK_HANDLER_PREFIX, true);

                    // If this lock handler requires the table prefix, add it to its properties.
                    if (lockHandler instanceof TablePrefixAware) {
                        tProps.setProperty(
                        		StdSchedulerFactory.PROP_TABLE_PREFIX, ((JobStoreSupport)js).getTablePrefix());
                    }
                    
                    try {
                        setBeanProps(lockHandler, tProps);
                    } catch (Exception e) {
                        initException = new SchedulerException("JobStore LockHandler class '" + lockHandlerClass
                                + "' props could not be configured.", e);
                        initException.setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
                        throw initException;
                    }
                    
                    ((JobStoreSupport)js).setLockHandler(lockHandler);
                    logger.info("Using custom data access locking (synchronization): " + lockHandlerClass);
                } catch (Exception e) {
                    initException = new SchedulerException("JobStore LockHandler class '" + lockHandlerClass
                            + "' could not be instantiated.", e);
                    initException.setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
                    throw initException;
                }
            }
        }

        // Set up any DataSources
        String[] dsNames = cfg.getPropertyGroups(StdSchedulerFactory.PROP_DATASOURCE_PREFIX);

        for (int i = 0; i < dsNames.length; i++) {
            PropertiesParser pp = new PropertiesParser(cfg.getPropertyGroup(
            		StdSchedulerFactory.PROP_DATASOURCE_PREFIX + "." + dsNames[i], true));

            String springDsInte = cfg.getStringProperty(
            		StdSchedulerFactory.PROP_DATASOURCE_PREFIX + "." + 
            		dsNames[i] + "." +
            		SpringSchedulerFactory.SPRING_DS_INTEGRATE);              
            
            String cpClass = pp.getStringProperty(StdSchedulerFactory.PROP_CONNECTION_PROVIDER_CLASS, null);

            // custom connectionProvider...
            if(cpClass != null) {
            	
                ConnectionProvider cp = null;
                try {
                    cp = (ConnectionProvider) loadHelper.loadClass(cpClass).newInstance();
                } catch (Exception e) {
                    initException = new SchedulerException("ConnectionProvider class '" + cpClass
                            + "' could not be instantiated.", e);
                    initException
                            .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
                    throw initException;
                }

                try {
                    // remove the class name, so it isn't attempted to be set
                    pp.getUnderlyingProperties().remove(
                    		StdSchedulerFactory.PROP_CONNECTION_PROVIDER_CLASS);
                    
                    setBeanProps(cp, pp.getUnderlyingProperties());
                } catch (Exception e) {
                    initException = new SchedulerException("ConnectionProvider class '" + cpClass
                            + "' props could not be configured.", e);
                    initException
                            .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
                    throw initException;
                }

                dbMgr = DBConnectionManager.getInstance();
                dbMgr.addConnectionProvider(dsNames[i], cp);
            } else {

                if (springDsInte != null && Boolean.parseBoolean(springDsInte)) {


                    try {
                    	SpringframeworkConnectionProvider cp = new SpringframeworkConnectionProvider();
                    	cp.setDataSource( this.getDataSource() );
                    	
                        dbMgr = DBConnectionManager.getInstance();
                        dbMgr.addConnectionProvider(dsNames[i], cp);
                    } catch (SQLException sqle) {
                        initException = new SchedulerException(
                                "Could not initialize DataSource: " + dsNames[i],
                                sqle);
                        throw initException;
                    }        
                
                }
                
            }
          
        }

        // Set up any SchedulerPlugins
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        String[] pluginNames = cfg.getPropertyGroups(StdSchedulerFactory.PROP_PLUGIN_PREFIX);
        SchedulerPlugin[] plugins = new SchedulerPlugin[pluginNames.length];
        for (int i = 0; i < pluginNames.length; i++) {
            Properties pp = cfg.getPropertyGroup(StdSchedulerFactory.PROP_PLUGIN_PREFIX + "."
                    + pluginNames[i], true);

            String plugInClass = pp.getProperty(StdSchedulerFactory.PROP_PLUGIN_CLASS, null);

            if (plugInClass == null) {
                initException = new SchedulerException(
                        "SchedulerPlugin class not specified for plugin '"
                                + pluginNames[i] + "'",
                        SchedulerException.ERR_BAD_CONFIGURATION);
                throw initException;
            }
            SchedulerPlugin plugin = null;
            try {
                plugin = (SchedulerPlugin)
                        loadHelper.loadClass(plugInClass).newInstance();
            } catch (Exception e) {
                initException = new SchedulerException(
                        "SchedulerPlugin class '" + plugInClass
                                + "' could not be instantiated.", e);
                initException
                        .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
                throw initException;
            }
            try {
                setBeanProps(plugin, pp);
            } catch (Exception e) {
                initException = new SchedulerException(
                        "JobStore SchedulerPlugin '" + plugInClass
                                + "' props could not be configured.", e);
                initException
                        .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
                throw initException;
            }
            
            plugins[i] = plugin;
        }

        // Set up any JobListeners
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        Class[] strArg = new Class[] { String.class };
        String[] jobListenerNames = cfg.getPropertyGroups(StdSchedulerFactory.PROP_JOB_LISTENER_PREFIX);
        JobListener[] jobListeners = new JobListener[jobListenerNames.length];
        for (int i = 0; i < jobListenerNames.length; i++) {
            Properties lp = cfg.getPropertyGroup(StdSchedulerFactory.PROP_JOB_LISTENER_PREFIX + "."
                    + jobListenerNames[i], true);
            
            String listenerClass = lp.getProperty(StdSchedulerFactory.PROP_LISTENER_CLASS, null);

            if (listenerClass == null) {
                initException = new SchedulerException(
                        "JobListener class not specified for listener '"
                                + jobListenerNames[i] + "'",
                        SchedulerException.ERR_BAD_CONFIGURATION);
                throw initException;
            }
            JobListener listener = null;
            try {
                listener = (JobListener)
                       loadHelper.loadClass(listenerClass).newInstance();
            } catch (Exception e) {
                initException = new SchedulerException(
                        "JobListener class '" + listenerClass
                                + "' could not be instantiated.", e);
                initException
                        .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
                throw initException;
            }
            try {
                Method nameSetter = listener.getClass().getMethod("setName", strArg);
                if(nameSetter != null) {
                    nameSetter.invoke(listener, new Object[] {jobListenerNames[i] } );
                }
                setBeanProps(listener, lp);
            } catch (Exception e) {
                initException = new SchedulerException(
                        "JobListener '" + listenerClass
                                + "' props could not be configured.", e);
                initException
                        .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
                throw initException;
            }
            jobListeners[i] = listener;
        }
               
        // Set up any TriggerListeners
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        String[] triggerListenerNames = cfg.getPropertyGroups(StdSchedulerFactory.PROP_TRIGGER_LISTENER_PREFIX);
        TriggerListener[] triggerListeners = new TriggerListener[triggerListenerNames.length];
        for (int i = 0; i < triggerListenerNames.length; i++) {
            Properties lp = cfg.getPropertyGroup(StdSchedulerFactory.PROP_TRIGGER_LISTENER_PREFIX + "."
                    + triggerListenerNames[i], true);
            
            String listenerClass = lp.getProperty(StdSchedulerFactory.PROP_LISTENER_CLASS, null);

            if (listenerClass == null) {
                initException = new SchedulerException(
                        "TriggerListener class not specified for listener '"
                                + triggerListenerNames[i] + "'",
                        SchedulerException.ERR_BAD_CONFIGURATION);
                throw initException;
            }
            TriggerListener listener = null;
            try {
                listener = (TriggerListener)
                       loadHelper.loadClass(listenerClass).newInstance();
            } catch (Exception e) {
                initException = new SchedulerException(
                        "TriggerListener class '" + listenerClass
                                + "' could not be instantiated.", e);
                initException
                        .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
                throw initException;
            }
            try {
                Method nameSetter = listener.getClass().getMethod("setName", strArg);
                if(nameSetter != null) {
                    nameSetter.invoke(listener, new Object[] {triggerListenerNames[i] } );
                }
                setBeanProps(listener, lp);
            } catch (Exception e) {
                initException = new SchedulerException(
                        "TriggerListener '" + listenerClass
                                + "' props could not be configured.", e);
                initException
                        .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION);
                throw initException;
            }
            triggerListeners[i] = listener;
        }
                
        
        // Fire everything up
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        JobRunShellFactory jrsf = null; // Create correct run-shell factory...

        if (userTXLocation != null) {
            UserTransactionHelper.setUserTxLocation(userTXLocation);
        }

        if (wrapJobInTx) {
            jrsf = new JTAJobRunShellFactory();
        } else {
            jrsf = new StdJobRunShellFactory();
        }

        if (autoId) {
            try {
                schedInstId = StdSchedulerFactory.DEFAULT_INSTANCE_ID;
                if (js instanceof JobStoreSupport) {
                    if(((JobStoreSupport)js).isClustered()) {
                        schedInstId = instanceIdGenerator.generateInstanceId();                    
                    }
                }
            } catch (Exception e) {
                logger.error("Couldn't generate instance Id!", e);
                throw new IllegalStateException(
                        "Cannot run without an instance id.");
            }
        }

        if (js instanceof JobStoreSupport) {
            JobStoreSupport jjs = (JobStoreSupport)js;
            jjs.setInstanceId(schedInstId);
            jjs.setDbRetryInterval(dbFailureRetry);
            if(threadsInheritInitalizersClassLoader)
            	jjs.setThreadsInheritInitializersClassLoadContext(threadsInheritInitalizersClassLoader);
        }
        
        QuartzSchedulerResources rsrcs = new QuartzSchedulerResources();
        rsrcs.setName(schedName);
        rsrcs.setThreadName(threadName);        
        rsrcs.setInstanceId(schedInstId);
        rsrcs.setJobRunShellFactory(jrsf);
        rsrcs.setMakeSchedulerThreadDaemon(makeSchedulerThreadDaemon);
        rsrcs.setThreadsInheritInitializersClassLoadContext(threadsInheritInitalizersClassLoader);
        rsrcs.setJMXExport(jmxExport);
        rsrcs.setJMXObjectName(jmxObjectName);
        	
        if (rmiExport) {
            rsrcs.setRMIRegistryHost(rmiHost);
            rsrcs.setRMIRegistryPort(rmiPort);
            rsrcs.setRMIServerPort(rmiServerPort);
            rsrcs.setRMICreateRegistryStrategy(rmiCreateRegistry);
            rsrcs.setRMIBindName(rmiBindName);
        }

        rsrcs.setThreadPool(tp);
        if(tp instanceof SimpleThreadPool) {
            ((SimpleThreadPool)tp).setThreadNamePrefix(schedName + "_Worker");
            if(threadsInheritInitalizersClassLoader)
            	((SimpleThreadPool)tp).setThreadsInheritContextClassLoaderOfInitializingThread(threadsInheritInitalizersClassLoader);
        }
        tp.initialize();
        
        rsrcs.setJobStore(js);

        // add plugins
        for (int i = 0; i < plugins.length; i++) {
            rsrcs.addSchedulerPlugin(plugins[i]);
        }

        schedCtxt = new SchedulingContext();
        schedCtxt.setInstanceId(rsrcs.getInstanceId());

        qs = new QuartzScheduler(rsrcs, schedCtxt, idleWaitTime, dbFailureRetry);

        // Create Scheduler ref...
        Scheduler scheduler = instantiate(rsrcs, qs);
        
        // set job factory if specified
        if(jobFactory != null) {
            qs.setJobFactory(jobFactory);
        }

        // Initialize plugins now that we have a Scheduler instance.
        for (int i = 0; i < plugins.length; i++) {
            plugins[i].initialize(pluginNames[i], scheduler);
        }
        
        // add listeners
        for (int i = 0; i < jobListeners.length; i++) {
            qs.addGlobalJobListener(jobListeners[i]);
        }
        for (int i = 0; i < triggerListeners.length; i++) {
            qs.addGlobalTriggerListener(triggerListeners[i]);
        }
        
        // set scheduler context data...
        Iterator itr = schedCtxtProps.keySet().iterator();
        while(itr.hasNext()) {
            String key = (String) itr.next();
            String val = schedCtxtProps.getProperty(key);
            
            scheduler.getContext().put(key, val);
        }
        
        // fire up job store, and runshell factory

        js.initialize(loadHelper, qs.getSchedulerSignaler());

        jrsf.initialize(scheduler, schedCtxt);

        logger.info(
                "Quartz scheduler '" + scheduler.getSchedulerName()
                        + "' initialized from " + propSrc);

        logger.info("Quartz scheduler version: " + qs.getVersion());

        // prevents the repository from being garbage collected
        qs.addNoGCObject(schedRep);
        // prevents the db manager from being garbage collected
        if (dbMgr != null) {
            qs.addNoGCObject(dbMgr);
        }

        schedRep.bind(scheduler);

        return scheduler;
    }
    

    protected Scheduler instantiate(QuartzSchedulerResources rsrcs, QuartzScheduler qs) {
        SchedulingContext schedCtxt = new SchedulingContext();
        schedCtxt.setInstanceId(rsrcs.getInstanceId());
        
        Scheduler scheduler = new StdScheduler(qs, schedCtxt);
        return scheduler;
    }    
    
	@Override
	public Collection getAllSchedulers() throws SchedulerException {
		// TODO Auto-generated method stub
		return SchedulerRepository.getInstance().lookupAll();
	}

	@Override
	public Scheduler getScheduler() throws SchedulerException {
		// TODO Auto-generated method stub
        if (cfg == null) {
            initialize();
        }
        
        SchedulerRepository schedRep = SchedulerRepository.getInstance();

        Scheduler sched = schedRep.lookup(getSchedulerName());

        if (sched != null) {
            if (sched.isShutdown()) {
                schedRep.remove(getSchedulerName());
            } else {
                return sched;
            }
        }

        sched = instantiate();

        return sched;
        

	}

	@Override
	public Scheduler getScheduler(String schedName) throws SchedulerException {
		// TODO Auto-generated method stub
        return SchedulerRepository.getInstance().lookup(schedName);
	}

	

    
}
